<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js 后处理通道演示</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>Three.js 后处理通道演示</h1>
            <p>展示多种后处理效果的组合使用</p>
        </header>
        
        <nav class="controls-panel">
            <h3>后处理控制</h3>
            
            <!-- 基础设置 -->
            <div class="control-section">
                <h4>基础设置</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enablePostProcessing" checked>
                        启用后处理
                    </label>
                </div>
                <div class="control-group">
                    <label>渲染分辨率</label>
                    <select id="resolution">
                        <option value="1">100% (原分辨率)</option>
                        <option value="0.75">75%</option>
                        <option value="0.5">50%</option>
                        <option value="0.25">25%</option>
                    </select>
                </div>
            </div>
            
            <!-- 发光效果 -->
            <div class="control-section">
                <h4>发光效果 (Bloom)</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enableBloom" checked>
                        启用发光
                    </label>
                </div>
                <div class="control-group">
                    <label>发光强度</label>
                    <input type="range" id="bloomStrength" min="0" max="3" step="0.1" value="1.5">
                    <span id="bloomStrengthValue">1.5</span>
                </div>
                <div class="control-group">
                    <label>发光半径</label>
                    <input type="range" id="bloomRadius" min="0" max="1" step="0.01" value="0.4">
                    <span id="bloomRadiusValue">0.4</span>
                </div>
                <div class="control-group">
                    <label>发光阈值</label>
                    <input type="range" id="bloomThreshold" min="0" max="1" step="0.01" value="0.85">
                    <span id="bloomThresholdValue">0.85</span>
                </div>
            </div>
            
            <!-- 胶片效果 -->
            <div class="control-section">
                <h4>胶片效果 (Film)</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enableFilm">
                        启用胶片效果
                    </label>
                </div>
                <div class="control-group">
                    <label>噪点强度</label>
                    <input type="range" id="filmNoise" min="0" max="1" step="0.01" value="0.35">
                    <span id="filmNoiseValue">0.35</span>
                </div>
                <div class="control-group">
                    <label>扫描线强度</label>
                    <input type="range" id="filmScanline" min="0" max="1" step="0.01" value="0.025">
                    <span id="filmScanlineValue">0.025</span>
                </div>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="filmGrayscale">
                        灰度模式
                    </label>
                </div>
            </div>
            
            <!-- 色彩校正 -->
            <div class="control-section">
                <h4>色彩校正</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enableColorCorrection">
                        启用色彩校正
                    </label>
                </div>
                <div class="control-group">
                    <label>亮度</label>
                    <input type="range" id="brightness" min="0" max="2" step="0.01" value="1">
                    <span id="brightnessValue">1.0</span>
                </div>
                <div class="control-group">
                    <label>对比度</label>
                    <input type="range" id="contrast" min="0" max="2" step="0.01" value="1">
                    <span id="contrastValue">1.0</span>
                </div>
                <div class="control-group">
                    <label>饱和度</label>
                    <input type="range" id="saturation" min="0" max="2" step="0.01" value="1">
                    <span id="saturationValue">1.0</span>
                </div>
            </div>
            
            <!-- 像素化效果 -->
            <div class="control-section">
                <h4>像素化效果</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enablePixelated">
                        启用像素化
                    </label>
                </div>
                <div class="control-group">
                    <label>像素大小</label>
                    <input type="range" id="pixelSize" min="2" max="20" step="1" value="6">
                    <span id="pixelSizeValue">6</span>
                </div>
            </div>
            
            <!-- 点阵屏幕效果 -->
            <div class="control-section">
                <h4>点阵屏幕效果</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enableDotScreen">
                        启用点阵屏幕
                    </label>
                </div>
                <div class="control-group">
                    <label>点阵缩放</label>
                    <input type="range" id="dotScreenScale" min="1" max="10" step="0.1" value="4">
                    <span id="dotScreenScaleValue">4.0</span>
                </div>
                <div class="control-group">
                    <label>点阵角度</label>
                    <input type="range" id="dotScreenAngle" min="0" max="6.28" step="0.1" value="1.57">
                    <span id="dotScreenAngleValue">1.57</span>
                </div>
            </div>
            
            <!-- 故障效果 -->
            <div class="control-section">
                <h4>故障效果</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enableGlitch">
                        启用故障效果
                    </label>
                </div>
                <div class="control-group">
                    <label>故障强度</label>
                    <input type="range" id="glitchWildness" min="0" max="1" step="0.01" value="0.2">
                    <span id="glitchWildnessValue">0.20</span>
                </div>
            </div>
            
            <!-- 半色调效果 -->
            <div class="control-section">
                <h4>半色调效果</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enableHalftone">
                        启用半色调
                    </label>
                </div>
                <div class="control-group">
                    <label>半色调半径</label>
                    <input type="range" id="halftoneRadius" min="1" max="10" step="0.1" value="4">
                    <span id="halftoneRadiusValue">4.0</span>
                </div>
                <div class="control-group">
                    <label>散射强度</label>
                    <input type="range" id="halftoneScatter" min="0" max="1" step="0.01" value="0">
                    <span id="halftoneScatterValue">0.00</span>
                </div>
            </div>
            
            <!-- 轮廓效果 -->
            <div class="control-section">
                <h4>轮廓效果</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enableOutline">
                        启用轮廓
                    </label>
                </div>
                <div class="control-group">
                    <label>边缘强度</label>
                    <input type="range" id="outlineEdgeStrength" min="0" max="10" step="0.1" value="3">
                    <span id="outlineEdgeStrengthValue">3.0</span>
                </div>
                <div class="control-group">
                    <label>边缘发光</label>
                    <input type="range" id="outlineEdgeGlow" min="0" max="1" step="0.1" value="0">
                    <span id="outlineEdgeGlowValue">0.0</span>
                </div>
                <div class="control-group">
                    <label>边缘厚度</label>
                    <input type="range" id="outlineEdgeThickness" min="0.1" max="5" step="0.1" value="1">
                    <span id="outlineEdgeThicknessValue">1.0</span>
                </div>
                <div class="control-group">
                    <label>脉冲周期</label>
                    <input type="range" id="outlinePulsePeriod" min="0" max="10" step="0.1" value="0">
                    <span id="outlinePulsePeriodValue">0.0</span>
                </div>
                <div class="control-group">
                    <label>可见边缘颜色</label>
                    <input type="color" id="outlineVisibleEdgeColor" value="#ffffff">
                </div>
                <div class="control-group">
                    <label>隐藏边缘颜色</label>
                    <input type="color" id="outlineHiddenEdgeColor" value="#190a05">
                </div>
            </div>
            
            <!-- 抗锯齿 -->
            <div class="control-section">
                <h4>抗锯齿 (FXAA)</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="enableFXAA" checked>
                        启用FXAA
                    </label>
                </div>
            </div>
            
            <!-- 场景控制 -->
            <div class="control-section">
                <h4>场景控制</h4>
                <div class="control-group">
                    <label>
                        <input type="checkbox" id="autoRotate" checked>
                        自动旋转
                    </label>
                </div>
                <div class="control-group">
                    <label>旋转速度</label>
                    <input type="range" id="rotationSpeed" min="0" max="0.05" step="0.001" value="0.01">
                    <span id="rotationSpeedValue">0.01</span>
                </div>
                <button id="resetCamera">重置相机</button>
                <button id="resetEffects">重置所有效果</button>
            </div>
            
            <!-- 引入通道流程使用说明 -->
            <div class="control-section usage-guide-section">
                <h4>引入通道流程使用说明</h4>
                
                <div class="usage-guide">
                    <h5>基本概念</h5>
                    <div class="concept-list">
                        <div class="concept-item">
                            <span class="concept-name">EffectComposer</span>
                            <span class="concept-desc">后处理效果合成器，管理所有通道的执行顺序</span>
                        </div>
                        <div class="concept-item">
                            <span class="concept-name">Pass</span>
                            <span class="concept-desc">后处理通道，每个通道实现特定的视觉效果</span>
                        </div>
                        <div class="concept-item">
                            <span class="concept-name">RenderTarget</span>
                            <span class="concept-desc">渲染目标，用于在通道间传递图像数据</span>
                        </div>
                    </div>
                    
                    <h5>通道类型</h5>
                    <div class="pass-types">
                        <div class="pass-type-item">
                            <strong>RenderPass</strong> - 基础渲染通道，必须作为第一个通道
                        </div>
                        <div class="pass-type-item">
                            <strong>EffectPass</strong> - 效果通道，如发光、模糊、颜色调整等
                        </div>
                        <div class="pass-type-item">
                            <strong>ShaderPass</strong> - 自定义着色器通道，用于实现特殊效果
                        </div>
                        <div class="pass-type-item">
                            <strong>OutputPass</strong> - 输出通道，通常作为最后一个通道
                        </div>
                    </div>
                    
                    <h5>使用步骤</h5>
                    <div class="usage-steps">
                        <div class="step-item">
                            <span class="step-number">1</span>
                            <span class="step-desc">创建EffectComposer实例</span>
                        </div>
                        <div class="step-item">
                            <span class="step-number">2</span>
                            <span class="step-desc">添加RenderPass作为第一个通道</span>
                        </div>
                        <div class="step-item">
                            <span class="step-number">3</span>
                            <span class="step-desc">按需添加各种效果通道</span>
                        </div>
                        <div class="step-item">
                            <span class="step-number">4</span>
                            <span class="step-desc">添加OutputPass作为最后通道</span>
                        </div>
                        <div class="step-item">
                            <span class="step-number">5</span>
                            <span class="step-desc">在渲染循环中调用composer.render()</span>
                        </div>
                    </div>
                </div>
                
                <div class="code-examples">
                    <h5>代码示例</h5>
                    
                    <div class="example-item">
                        <h6>基础设置</h6>
                        <pre class="example-code"><code>// 创建合成器
const composer = new EffectComposer(renderer);

// 添加基础渲染通道
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);</code></pre>
                    </div>
                    
                    <div class="example-item">
                        <h6>添加发光效果</h6>
                        <pre class="example-code"><code>// 发光效果
const bloomPass = new UnrealBloomPass(
    new THREE.Vector2(window.innerWidth, window.innerHeight),
    1.5,  // 强度
    0.4,  // 半径
    0.85  // 阈值
);
composer.addPass(bloomPass);</code></pre>
                    </div>
                    
                    <div class="example-item">
                        <h6>自定义着色器通道</h6>
                        <pre class="example-code"><code>// 自定义着色器
const customShader = {
    uniforms: {
        tDiffuse: { value: null },
        uTime: { value: 0.0 }
    },
    vertexShader: `...`,
    fragmentShader: `...`
};

const customPass = new ShaderPass(customShader);
composer.addPass(customPass);</code></pre>
                    </div>
                    
                    <div class="example-item">
                        <h6>渲染循环</h6>
                        <pre class="example-code"><code>function animate() {
    requestAnimationFrame(animate);
    
    // 更新场景
    updateScene();
    
    // 使用合成器渲染
    composer.render();
}</code></pre>
                    </div>
                </div>
                
                <div class="usage-tips">
                    <h5>使用技巧</h5>
                    <ul>
                        <li><strong>性能优化：</strong>合理控制通道数量，避免过度使用高消耗效果</li>
                        <li><strong>通道顺序：</strong>某些效果的顺序会影响最终结果，需要合理安排</li>
                        <li><strong>参数调试：</strong>使用GUI控制面板实时调整参数，找到最佳效果</li>
                        <li><strong>兼容性：</strong>注意不同设备的WebGL支持情况，提供降级方案</li>
                        <li><strong>内存管理：</strong>及时释放不需要的渲染目标和纹理资源</li>
                    </ul>
                </div>
            </div>
        </nav>
        
        <main class="canvas-container">
            <canvas id="canvas"></canvas>
            
            <!-- 效果信息面板 -->
            <div class="info-panel">
                <h4>当前效果链</h4>
                <div id="effect-chain">
                    <div class="effect-item active" data-effect="render">RenderPass</div>
                    <div class="effect-item active" data-effect="bloom">BloomPass</div>
                    <div class="effect-item" data-effect="film">FilmPass</div>
                    <div class="effect-item" data-effect="color">ColorCorrection</div>
                    <div class="effect-item" data-effect="pixelated">PixelatedPass</div>
                    <div class="effect-item" data-effect="dotscreen">DotScreenPass</div>
                    <div class="effect-item" data-effect="glitch">GlitchPass</div>
                    <div class="effect-item" data-effect="halftone">HalftonePass</div>
                    <div class="effect-item" data-effect="outline">OutlinePass</div>
                    <div class="effect-item active" data-effect="fxaa">FXAAPass</div>
                    <div class="effect-item active" data-effect="output">OutputPass</div>
                </div>
                <div class="performance-info">
                    <p>FPS: <span id="fps">60</span></p>
                    <p>渲染时间: <span id="renderTime">16.7ms</span></p>
                </div>
            </div>
        </main>
        
        <!-- 右侧通道信息面板 -->
        <aside class="pass-info-panel">
            <h3>通道信息</h3>
            
            <div class="pass-details">
                <div class="pass-header">
                    <h4 id="currentPassName">RenderPass</h4>
                    <span class="pass-status active" id="currentPassStatus">激活</span>
                </div>
                
                <div class="pass-description">
                    <h5>作用说明</h5>
                    <p id="passDescription">
                        RenderPass 是后处理管道的基础通道，负责将3D场景渲染到纹理缓冲区中，为后续的后处理效果提供输入图像。
                    </p>
                </div>
                
                <div class="pass-properties">
                    <h5>主要属性</h5>
                    <div class="property-list" id="passProperties">
                        <div class="property-item">
                            <span class="property-name">enabled</span>
                            <span class="property-value">true</span>
                            <span class="property-desc">是否启用此通道</span>
                        </div>
                        <div class="property-item">
                            <span class="property-name">needsSwap</span>
                            <span class="property-value">true</span>
                            <span class="property-desc">是否需要交换缓冲区</span>
                        </div>
                        <div class="property-item">
                            <span class="property-name">clear</span>
                            <span class="property-value">true</span>
                            <span class="property-desc">渲染前是否清除缓冲区</span>
                        </div>
                        <div class="property-item">
                            <span class="property-name">renderToScreen</span>
                            <span class="property-value">false</span>
                            <span class="property-desc">是否直接渲染到屏幕</span>
                        </div>
                    </div>
                </div>
                
                <div class="pass-parameters">
                    <h5>当前参数</h5>
                    <div class="parameter-list" id="passParameters">
                        <div class="parameter-item">
                            <span class="param-name">scene</span>
                            <span class="param-value">Scene</span>
                        </div>
                        <div class="parameter-item">
                            <span class="param-name">camera</span>
                            <span class="param-value">PerspectiveCamera</span>
                        </div>
                    </div>
                </div>
                
                <div class="pass-usage">
                    <h5>使用说明</h5>
                    <div class="usage-content" id="passUsage">
                        <p>RenderPass 通常作为后处理链的第一个通道，将场景渲染结果传递给后续的效果通道。</p>
                        <code>const renderPass = new RenderPass(scene, camera);</code>
                        
                        <h6>详细说明</h6>
                        <ul>
                            <li><strong>基础渲染：</strong>负责将3D场景渲染到帧缓冲区，是所有后处理效果的基础</li>
                            <li><strong>性能影响：</strong>作为第一个通道，其性能直接影响整个渲染管线</li>
                            <li><strong>配置要点：</strong>通常不需要特殊配置，但要确保场景和相机设置正确</li>
                        </ul>
                        
                        <h6>使用技巧</h6>
                        <ul>
                            <li>确保在后处理链的最开始位置</li>
                            <li>可以通过调整相机参数来优化渲染效果</li>
                            <li>配合其他通道使用时注意渲染顺序</li>
                        </ul>
                        
                        <h6>常见问题</h6>
                        <ul>
                            <li><strong>黑屏问题：</strong>检查场景中是否有光源和几何体</li>
                            <li><strong>性能问题：</strong>考虑降低渲染分辨率或优化场景复杂度</li>
                            <li><strong>颜色异常：</strong>检查材质和光照设置</li>
                        </ul>
                    </div>
                </div>
            </div>
        </aside>
    </div>
    
    <!-- 代码弹窗模态框 -->
    <div id="codeModal" class="modal">
        <div class="modal-content">
            <div class="modal-header">
                <h3>📚 Three.js 后处理通道完整使用流程</h3>
                <span class="close" onclick="closeCodeModal()">&times;</span>
            </div>
            <div class="modal-body">
                <div class="modal-code-header">
                    <span class="modal-code-title">完整实现代码</span>
                    <button class="modal-copy-btn" onclick="copyModalCode()">📋 复制代码</button>
                </div>
                <pre class="modal-code-content"><code id="modal-complete-code">import * as THREE from 'three';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { BloomPass } from 'three/addons/postprocessing/BloomPass.js';
import { FilmPass } from 'three/addons/postprocessing/FilmPass.js';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { FXAAShader } from 'three/addons/shaders/FXAAShader.js';

// 1. 创建渲染器、场景、相机等基础组件
const renderer = new THREE.WebGLRenderer();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera();

// 2. 创建效果合成器
const composer = new EffectComposer(renderer);

// 3. 添加基础渲染通道
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

// 4. 添加发光效果通道
const bloomPass = new BloomPass(1.5, 25, 0.1);
composer.addPass(bloomPass);

// 5. 添加胶片颗粒通道
const filmPass = new FilmPass(0.35, 0.025, 648, false);
composer.addPass(filmPass);

// 6. 添加抗锯齿通道
const fxaaPass = new ShaderPass(FXAAShader);
fxaaPass.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight);
composer.addPass(fxaaPass);

// 7. 设置最终输出通道
fxaaPass.renderToScreen = true; // 标记为最终输出

// 8. 在动画循环中使用合成器渲染
function animate() {
  requestAnimationFrame(animate);
  
  // 更新特定通道参数
  filmPass.uniforms['time'].value += 0.01;
  
  composer.render(); // 替代 renderer.render(scene, camera)
}
animate();</code></pre>
                
                <div class="modal-notes">
                    <h4>💡 重要说明</h4>
                    <ul>
                        <li><strong>通道顺序：</strong>RenderPass 必须是第一个通道，最后一个通道需要设置 renderToScreen = true</li>
                        <li><strong>性能优化：</strong>只启用需要的通道，合理设置参数，考虑使用较低的渲染分辨率</li>
                        <li><strong>调试技巧：</strong>逐个启用通道来定位问题，检查 uniform 参数是否正确设置</li>
                        <li><strong>常见通道类型：</strong>RenderPass(基础渲染)、BloomPass(发光效果)、FilmPass(胶片颗粒)、ShaderPass(自定义着色器)</li>
                        <li><strong>兼容性：</strong>确保着色器与目标设备兼容，注意 WebGL 版本差异</li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
    
    <script type="importmap">
    {
        "imports": {
            "three": "../three.js/build/three.module.js",
            "three/addons/": "../three.js/examples/jsm/"
        }
    }
    </script>
    <script type="module" src="main.js"></script>
</body>
</html>